use rocket;
use rocket::State;
use rocket::response::{NamedFile, status};
use rocket_contrib::JSON;
use rocket::http::Status;
use std::path::{Path, PathBuf};
use serde_json;
use std::ops::Deref;
use std::io::Read;
use std::default::Default;
use serde;
use reqwest;

#[get("/")]
fn index() -> Option<NamedFile> {
    NamedFile::open(Path::new("static/index.html")).ok()
}

#[get("/<file..>")]
fn files(file: PathBuf) -> Option<NamedFile> {
    NamedFile::open(Path::new("static/").join(file)).ok()
}

pub type Message = String;

#[serde(default)]
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct EventRequestFile {
    id: String,
    name: String,
    size: usize,
    busid: usize,
}

#[serde(default)]
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct PostResultWrap<T: serde::Deserialize + Default> {
    status: String,
    retcode: usize,
    data: T,
}

#[serde(default)]
#[derive(Serialize, Deserialize, Default)]
pub struct EventRequest {
    /// 上报类型
    /// 私聊消息:message
    /// 群消息:  message
    /// 讨论组:  message
    /// 群文件上传: event
    /// 加好友请求: request
    pub post_type: String,
    /// 消息类型
    /// 私聊消息:private
    /// 群消息:  group
    /// 讨论组:  discuss
    pub message_type: String,
    /// 消息子类型
    /// 私聊消息:friend,group,discuss,other
    /// 群消息:  无此字段
    /// 群管理成员变动: set/unset
    /// 群成员减少: "leave"、"kick"、"kick_me"
    /// 群成员增加: "approve"、"invite"
    /// 加群请求: "add"、"invite"
    pub sub_type: String,
    /// 发送者QQ号码
    pub user_id: usize,
    /// 组id
    pub group_id: usize,
    /// 匿名用户显示名
    pub anonymous: String,
    /// 匿名用户 flag，在调用禁言 API 时需要传入
    pub anonymous_flag: String,
    /// 只有讨论组有效
    /// 讨论组ID
    pub discuss_id: usize,
    /// 事件
    /// 群文件上传: group_upload
    /// 群管理成员变动: group_admin
    /// 群成员减少: group_decrease
    /// 群成员增加: group_increase
    /// 好友增加: friend_add
    pub event: String,
    /// 消息内容
    pub message: Message,
    /// 文件信息
    pub file: EventRequestFile,
    /// 操作者ID
    pub operator_id: usize,
    /// 请求类型
    /// 加好友请求: friend
    /// 加群请求:   group
    pub request_type: String,
    /// flag
    pub flag: String,
}

#[derive(Serialize, Deserialize, Default)]
pub struct EventResponse {
    pub block: Option<bool>,
    /// 私聊消息: 要回复的内容
    /// 群消息:   要回复的内容
    pub reply: Option<Message>,
    /// 群和讨论组有效
    /// 是否at发送者（默认为true）
    pub at_sender: Option<bool>,
    /// 只有群有效
    /// 把发送者踢出群
    pub kick: Option<bool>,
    /// 只有群有效
    /// 禁言发送者30分钟（默认为false）
    pub ban: Option<bool>,
    /// 添加好友(群)是否同意
    pub approve: Option<bool>,
    /// 添加好友后的备注
    pub remark: Option<String>,
    /// 拒绝理由
    pub reason: Option<String>,
}

#[post("/api/event", format = "application/json", data = "<req>")]
fn event(
    req: JSON<EventRequest>,
    cqp: State<CqpEvent>,
    act: State<CqpAction>,
) -> status::Custom<Option<JSON<EventResponse>>> {
    let req = req.deref();
    debug!(
        "got event, {}",
        serde_json::to_string(req).unwrap()
    );
    let handlers = &cqp.handlers;
    let act = act.deref();
    match (
        &req.post_type[..],
        &req.message_type[..],
        &req.sub_type[..],
        &req.event[..],
        &req.request_type[..],
    ) {
        ("message", "private", sub, _, _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_private_message(act, sub, req.user_id, &req.message) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("message", "group", _, _, _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_group_message(
                    act,
                    req.group_id,
                    req.user_id,
                    &req.anonymous,
                    &req.anonymous_flag,
                    &req.message,
                ) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("message", "discuss", _, _, _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_discuss_message(act, req.discuss_id, req.user_id, &req.message) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("event", _, _, "group_upload", _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_group_upload(act, req.group_id, req.user_id, &req.file) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("event", _, "set", "group_admin", _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_group_admin_set(act, req.group_id, req.user_id) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("event", _, "unset", "group_admin", _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_group_admin_unset(act, req.group_id, req.user_id) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("event", _, sub, "group_decrease", _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_group_decrease(act, sub, req.group_id, req.user_id, req.operator_id) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("event", _, sub, "group_increase", _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_group_increase(act, sub, req.group_id, req.user_id, req.operator_id) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("event", _, _, "friend_add", _) => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_friend_add(act, req.user_id) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("request", _, _, _, "friend") => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_friend(act, req.user_id, &req.message, &req.flag) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        ("request", _, sub, _, "group") => {
            for h in handlers.iter() {
                if let Some(rsp) = h.on_group_add(act, sub, req.group_id, req.user_id, &req.message, &req.flag) {
                    return status::Custom(Status::Ok, Some(JSON(rsp)));
                }
            }
        }
        _ => {}
    }
    return return status::Custom(Status::NoContent, None);
}

pub trait CqpHandler: Send + Sync {
    fn on_private_message(
        &self,
        api: &CqpActionExt,
        sub_type: &str,
        user_id: usize,
        msg: &Message,
    ) -> Option<EventResponse> {
        None
    }
    /// group id, sender id, anonymous, anomyous flag, message
    fn on_group_message(
        &self,
        api: &CqpActionExt,
        group: usize,
        sender: usize,
        anonymous: &str,
        anomoyous_flag: &str,
        msg: &Message,
    ) -> Option<EventResponse> {
        None
    }
    /// discuss id, sender id, message
    fn on_discuss_message(
        &self,
        api: &CqpActionExt,
        discuss: usize,
        sender: usize,
        msg: &Message,
    ) -> Option<EventResponse> {
        None
    }
    /// group id, user id, file object
    fn on_group_upload(
        &self,
        api: &CqpActionExt,
        group: usize,
        user_id: usize,
        file: &EventRequestFile,
    ) -> Option<EventResponse> {
        None
    }
    /// gourp id, user id
    fn on_group_admin_set(
        &self,
        api: &CqpActionExt,
        group: usize,
        user_id: usize,
    ) -> Option<EventResponse> {
        None
    }
    /// gourp id, user id
    fn on_group_admin_unset(
        &self,
        api: &CqpActionExt,
        group_id: usize,
        user_id: usize,
    ) -> Option<EventResponse> {
        None
    }
    /// "leave"、"kick"、"kick_me", group id, user id, operator id
    fn on_group_decrease(
        &self,
        api: &CqpActionExt,
        sub_type: &str,
        group_id: usize,
        user_id: usize,
        operator_id: usize,
    ) -> Option<EventResponse> {
        None
    }
    /// "approve"、"invite", group id, user id, operator id
    fn on_group_increase(
        &self,
        api: &CqpActionExt,
        sub_type: &str,
        group_id: usize,
        user_id: usize,
        operator_id: usize,
    ) -> Option<EventResponse> {
        None
    }
    /// user id
    fn on_friend_add(&self, api: &CqpActionExt, user_id: usize) -> Option<EventResponse> {
        None
    }
    /// user id, message, flag
    fn on_friend(
        &self,
        api: &CqpActionExt,
        user_id: usize,
        msg: &Message,
        flag: &str,
    ) -> Option<EventResponse> {
        None
    }
    /// "add"、"invite", group id, user id, message, flag
    fn on_group_add(
        &self,
        api: &CqpActionExt,
        sub_type: &str,
        group_id: usize,
        user_id: usize,
        msg: &Message,
        flag: &str,
    ) -> Option<EventResponse> {
        None
    }
}

pub struct CqpAction {
    uri: String,
    client: reqwest::Client,
}

pub struct CqpEvent {
    handlers: Vec<Box<CqpHandler>>,
}

#[derive(Deserialize, Debug, Default)]
pub struct GroupMemberInfo {
    group_id: usize, // 	群号
    user_id: usize, // 	QQ 号
    nickname: String, // 昵称
    card: String, // 	群名片／备注
    sex: String, // 	性别，male 或 female 或 unknown
    age: usize, // 	年龄
    area: String, // 	地区
    join_time: usize, // 	加群时间戳
    last_sent_time: usize, // 最后发言时间戳
    level: String, // 成员等级
    role: String, // 角色，owner 或 admin 或 member
    unfriendly: bool, // 是否不良记录成员
    title: String, // 专属头衔
    title_expire_time: usize, // 专属头衔过期时间戳
    card_changeable: bool, // 是否允许修改群名片
}

#[derive(Deserialize, Debug, Default)]
pub struct GroupInfo {
    group_id: usize,
    group_name: String,
}

#[derive(Deserialize, Debug, Default)]
pub struct StrangerInfo {
    user_id: usize,
    nickname: String,
    sex: String,
    age: usize,
}

pub trait CqpActionExt {
    // user_id	    number	QQ 号
    // nickname	string	QQ 昵称
    fn get_login_info(&self) -> Option<(usize, String)>;
    fn send_private_msg(&self, user_id: usize, message: Message, is_raw: bool);
    fn send_group_msg(&self, group_id: usize, message: Message, is_raw: bool);
    fn send_discuss_msg(&self, discuss_id: usize, message: Message, is_raw: bool);
    // user_id	number	-	对方 QQ 号
    // times	number	1	赞的次数，每个好友每天最多 10 次
    fn send_like(&self, user_id: usize, times: u8);
    fn set_group_kick(&self, group_id: usize, user_id: usize, reject_add_request: bool);
    fn set_group_ban(&self, group_id: usize, user_id: usize, duration_s: u32);
    fn set_group_anonymous_ban(&self, group_id: usize, flag: String, duration_s: u32);
    fn set_group_whole_ban(&self, group_id: usize, enable: bool);
    fn set_group_admin(&self, group_id: usize, user_id: usize, enable: bool);
    fn set_group_anonymous(&self, group_id: usize, enable: bool);
    fn set_group_card(&self, group_id: usize, user_id: usize, card: String);
    fn set_group_leave(&self, group_id: usize, is_dismiss: bool);
    fn set_group_special_title(
        &self,
        group_id: usize,
        user_id: usize,
        special_title: String,
        duration_s: usize,
    );
    fn set_discuss_leave(&self, discuss_id: usize);
    fn set_friend_add_request(&self, flag: String, approve: bool, remark: String);
    // flag	string	-	加好友请求的 flag（需从上报的数据中获得）
    // type	string	-	add 或 invite，请求类型（需要和上报消息中的 sub_type 字段相符）
    // approve	bool	true	是否同意请求／邀请
    // reason	string	空	拒绝理由（仅在拒绝时有效）
    fn set_group_add_request(&self, flag: String, _type: String, approve: bool, reason: String);
    // id, name
    fn get_group_list(&self) -> Option<Vec<GroupInfo>>;
    fn get_group_member_info(
        &self,
        group_id: usize,
        user_id: usize,
        no_cache: bool,
    ) -> Option<GroupMemberInfo>;

    fn get_group_member_list(&self, group_id: usize) -> Option<Vec<GroupMemberInfo>>;
    // user_id	number	QQ 号
    // nickname	string	昵称
    // sex	string	性别，male 或 female 或 unknown
    // age	number	年龄
    fn get_stranger_info(&self, user_id: usize, no_cache: bool) -> Option<StrangerInfo>;

    fn get_cookies(&self) -> Option<String>;

    fn get_csrf_token(&self) -> Option<usize>;
}

#[derive(Serialize, Deserialize, Default)]
pub struct EmptyParams;

type Ert = Option<EmptyParams>;

impl CqpEvent {
    pub fn new() -> CqpEvent {
        CqpEvent { handlers: Vec::new() }
    }

    pub fn insert_handler(&mut self, h: Box<CqpHandler>) {
        self.handlers.push(h);
    }
}

impl CqpAction {
    pub fn new(api_uri: String) -> CqpAction {
        CqpAction {
            uri: api_uri,
            client: reqwest::Client::new().unwrap(),
        }
    }

    pub fn post<S, D>(&self, url: &str, data: &S) -> Option<D>
    where
        S: serde::Serialize,
        D: serde::Deserialize + Default,
    {
        let mut headers = reqwest::header::Headers::new();
        headers.set_raw("Content-Type ", vec![b"application/json".to_vec()]);
        let res = self.client
            .get(&format!("{}{}", self.uri, url))
            .headers(headers)
            .body(serde_json::to_string_pretty(data).unwrap())
            .send();
        if let Err(e) = res {
            error!("fail to post, {:?}", e);
            return None;
        }
        let mut res = res.unwrap();
        if res.status().is_success() {
            let mut objstr = String::new();
            if let Ok(_) = res.read_to_string(&mut objstr) {
                let rt: PostResultWrap<D> = serde_json::from_str(&objstr).unwrap();
                return Some(rt.data);
            } else {
                return None;
            }
        }
        None
    }
}

impl CqpActionExt for CqpAction {
    fn get_login_info(&self) -> Option<(usize, String)> {
        #[derive(Deserialize, Debug, Default)]
        struct LoginInfoResponse {
            user_id: usize,
            nickname: String,
        }
        let rt: Option<LoginInfoResponse> = self.post("/get_login_info", &EmptyParams);
        if rt.is_none() {
            return None;
        }
        let rt = rt.unwrap();
        Some((rt.user_id, rt.nickname))
    }

    fn send_private_msg(&self, user_id: usize, message: Message, is_raw: bool) {
        let _: Ert = self.post(
            "/send_private_msg",
            &json!({
            "user_id": user_id,
            "message": message,
            "is_raw": is_raw,
        }),
        );
    }
    fn send_group_msg(&self, group_id: usize, message: Message, is_raw: bool) {
        let _: Ert = self.post(
            "/send_group_msg",
            &json!({
            "group_id": group_id,
            "message": message,
            "is_raw": is_raw,
        }),
        );
    }
    fn send_discuss_msg(&self, discuss_id: usize, message: Message, is_raw: bool) {
        let _: Ert = self.post(
            "send_discuss_msg",
            &json!({
            "discuss_id":discuss_id,
            "message":message,
            "is_raw":is_raw,
        }),
        );
    }
    // user_id	number	-	对方 QQ 号
    // times	number	1	赞的次数，每个好友每天最多 10 次
    fn send_like(&self, user_id: usize, times: u8) {
        let _: Ert = self.post(
            "/send_like",
            &json!({
            "user_id": user_id,
            "times": times,
        }),
        );
    }
    fn set_group_kick(&self, group_id: usize, user_id: usize, reject_add_request: bool) {
        let _: Ert = self.post(
            "/set_group_kick",
            &json!({
            "group_id" : group_id,
            "user_id" :user_id,
            "reject_add_request":reject_add_request,
        }),
        );
    }
    fn set_group_ban(&self, group_id: usize, user_id: usize, duration_s: u32) {
        let _: Ert = self.post(
            "set_group_ban",
            &json!({
            "group_id":group_id,
            "user_id":user_id,
            "duration":duration_s,
        }),
        );
    }
    fn set_group_anonymous_ban(&self, group_id: usize, flag: String, duration_s: u32) {
        let _: Ert = self.post(
            "/set_group_anonymous_ban",
            &json!({
            "group_id": group_id,
            "flag": flag,
            "duration": duration_s,
        }),
        );
    }
    fn set_group_whole_ban(&self, group_id: usize, enable: bool) {
        let _: Ert = self.post(
            "/set_group_whole_ban",
            &json!({
            "group_id": group_id,
            "enable":enable,
        }),
        );
    }
    fn set_group_admin(&self, group_id: usize, user_id: usize, enable: bool) {
        let _: Ert = self.post(
            "/set_group_admin",
            &json!({
            "group_id": group_id,
            "user_id": user_id,
            "enable": enable,
        }),
        );
    }
    fn set_group_anonymous(&self, group_id: usize, enable: bool) {
        let _: Ert = self.post(
            "/set_group_anonymous",
            &json!({
            "group_id": group_id,
            "enable": enable,
        }),
        );
    }
    fn set_group_card(&self, group_id: usize, user_id: usize, card: String) {
        let _: Ert = self.post(
            "/set_group_card",
            &json!({
            "group_id": group_id,
            "user_id": user_id,
            "card": card,
        }),
        );
    }
    fn set_group_leave(&self, group_id: usize, is_dismiss: bool) {
        let _: Ert = self.post(
            "/set_group_leave",
            &json!({
            "group_id": group_id,
            "is_dismiss":is_dismiss,
        }),
        );
    }
    fn set_group_special_title(
        &self,
        group_id: usize,
        user_id: usize,
        special_title: String,
        duration_s: usize,
    ) {
        let _: Ert = self.post(
            "/set_group_special_title",
            &json!({
            "group_id": group_id,
            "user_card":user_id,
            "special_title": special_title,
            "duration":duration_s,
        }),
        );
    }
    fn set_discuss_leave(&self, discuss_id: usize) {
        let _: Ert = self.post(
            "/set_discuss_leave",
            &json!({
            "discuss_id": discuss_id,
        }),
        );
    }
    fn set_friend_add_request(&self, flag: String, approve: bool, remark: String) {
        let _: Ert = self.post(
            "/set_friend_add_request",
            &json!({
            "flag":flag,
            "approve": approve,
            "remark":remark,
        }),
        );
    }
    // flag	string	-	加好友请求的 flag（需从上报的数据中获得）
    // type	string	-	add 或 invite，请求类型（需要和上报消息中的 sub_type 字段相符）
    // approve	bool	true	是否同意请求／邀请
    // reason	string	空	拒绝理由（仅在拒绝时有效）
    fn set_group_add_request(&self, flag: String, _type: String, approve: bool, reason: String) {
        let _: Ert = self.post(
            "/set_group_add_request",
            &json!({
            "flag": flag,
            "type":_type,
            "approve":approve,
            "reason":reason,
        }),
        );
    }
    // id, name
    fn get_group_list(&self) -> Option<Vec<GroupInfo>> {
        self.post("/get_group_list", &EmptyParams)
    }
    fn get_group_member_info(
        &self,
        group_id: usize,
        user_id: usize,
        no_cache: bool,
    ) -> Option<GroupMemberInfo> {
        self.post(
            "/get_group_member_info",
            &json!({
            "group_id" : group_id,
            "user_id" :user_id,
            "no_cache": no_cache,
        }),
        )
    }

    fn get_group_member_list(&self, group_id: usize) -> Option<Vec<GroupMemberInfo>> {
        self.post(
            "/get_group_member_list",
            &json!({
            "group_id": group_id,
        }),
        )
    }
    // user_id	number	QQ 号
    // nickname	string	昵称
    // sex	string	性别，male 或 female 或 unknown
    // age	number	年龄
    fn get_stranger_info(&self, user_id: usize, no_cache: bool) -> Option<StrangerInfo> {
        self.post(
            "/get_stranger_info",
            &json!({
            "user_id":user_id,
            "no_cache": no_cache,
        }),
        )
    }

    fn get_cookies(&self) -> Option<String> {
        self.post("/get_cookies", &EmptyParams)
    }

    fn get_csrf_token(&self) -> Option<usize> {
        self.post("/get_csrf_token", &EmptyParams)
    }
}

pub fn loop_web(holder: CqpEvent, action: CqpAction) {
    rocket::ignite()
        .manage(holder)
        .manage(action)
        .mount("/", routes![index, files, event])
        .launch();
}
